/*
   Copyright (c) 2016 VMware, Inc. All Rights Reserved.
   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
*/

// Package config provide methods to get the configurations reqruied by code in src/common
package config

import (
	"fmt"
	"os"
	"strings"
)

// ConfLoader is the interface to load configurations
//定义配置文件ConfLoader接口
type ConfLoader interface {
	// Load will load configuration from different source into a string map, the values in the map will be parsed in to configurations.
	//Load方法
	// 参数:map[string]string 存放配置文件
	//参数:error 用于返回错误,如load配置文件失败
	Load() (map[string]string, error)
}

// EnvConfigLoader loads the config from env vars.
//定义获取环境变量的结构体
type EnvConfigLoader struct {
	//获取环境变量的所有key
	Keys []string
}

// Load ...
//实现ConfLoader的Load方法并获取key与之对应的环境变量
//参数:map[string]string 用于保存环境变量的key和value 如:JAVA_HOME = D:\\java\bin
//参数:error 返回错误信息
//我刚开始在思考Load()方法返回的nil都写死了,为何还要error返回参数呢？
//os.Getenv()返回时获取的本地变量,该方法一般不会出错,但是既然是接口肯定要考虑以后的扩展,试想从网络load配置文件会遇到多种情况,如网络不通等，
// 这时候error还是非常有必要的
func (ec *EnvConfigLoader) Load() (map[string]string, error) {
	//初始化map
	m := make(map[string]string)
	//遍历所有环境变量的key
	for _, k := range ec.Keys {
		//获取环境变量,并存放到map容器里
		m[k] = os.Getenv(k)
	}
	return m, nil
}

// ConfParser ...
//配置文件转换接口
type ConfParser interface {

	//Parse parse the input raw map into a config map
	//转换
	//参数:raw 原始配置数据,部分环境变量可能存在空的情况
	//参数:config 配置数据,如果raw为空那么就给定默认值
	Parse(raw map[string]string, config map[string]interface{}) error
}

// Config wraps a map for the processed configuration values,
// and loader parser to read configuration from external source and process the values.
//定义配置结构体，用于load配置和解析配置
type Config struct {
	//
	Config map[string]interface{}
	//加载器:用于加载配置文件
	Loader ConfLoader
	//解析器:用于解析配置文件
	Parser ConfParser
}

// Load reload the configurations
//定义load方法
func (conf *Config) Load() error {
	//获取配置
	rawMap, err := conf.Loader.Load()
	if err != nil {
		return err
	}
	//解析配置
	err = conf.Parser.Parse(rawMap, conf.Config)
	return err
}

// MySQLSetting wraps the settings of a MySQL DB
type MySQLSetting struct {
	Database string
	User     string
	Password string
	Host     string
	Port     string
}

// SQLiteSetting wraps the settings of a SQLite DB
type SQLiteSetting struct {
	FilePath string
}

//通用解析结构体
type commonParser struct{}

// Parse parses the db settings, veryfy_remote_cert, ext_endpoint, token_endpoint
//实现configParse接口
//作用:获取环境变量数据,如果环境变量的值为空那么就给定默认值
func (cp *commonParser) Parse(raw map[string]string, config map[string]interface{}) error {
	db := strings.ToLower(raw["DATABASE"])
	if db == "mysql" || db == "" {
		db = "mysql"
		mySQLDB := raw["MYSQL_DATABASE"]
		if len(mySQLDB) == 0 {
			mySQLDB = "registry"
		}
		setting := MySQLSetting{
			mySQLDB,
			raw["MYSQL_USR"],
			raw["MYSQL_PWD"],
			raw["MYSQL_HOST"],
			raw["MYSQL_PORT"],
		}
		config["mysql"] = setting
	} else if db == "sqlite" {
		f := raw["SQLITE_FILE"]
		if len(f) == 0 {
			f = "registry.db"
		}
		setting := SQLiteSetting{
			f,
		}
		config["sqlite"] = setting
	} else {
		return fmt.Errorf("Invalid DB: %s", db)
	}
	config["database"] = db

	//By default it's true
	config["verify_remote_cert"] = raw["VERIFY_REMOTE_CERT"] != "off"

	config["ext_endpoint"] = raw["EXT_ENDPOINT"]
	config["token_endpoint"] = raw["TOKEN_ENDPOINT"]
	config["log_level"] = raw["LOG_LEVEL"]
	return nil
}

//定义config 供外界调用
var commonConfig *Config

//初始化配置
func init() {
	//要获取的环境变量的keys
	commonKeys := []string{"DATABASE", "MYSQL_DATABASE", "MYSQL_USR", "MYSQL_PWD", "MYSQL_HOST", "MYSQL_PORT", "SQLITE_FILE", "VERIFY_REMOTE_CERT", "EXT_ENDPOINT", "TOKEN_ENDPOINT", "LOG_LEVEL"}
	commonConfig = &Config{
		//初始化map,用于存放最终的配置数据
		Config: make(map[string]interface{}),
		Loader: &EnvConfigLoader{Keys: commonKeys},
		Parser: &commonParser{},
	}
	//加载配置文件
	if err := commonConfig.Load(); err != nil {
		panic(err)
	}
}

// Reload will reload the configuration.
//重新加载配置
func Reload() error {
	return commonConfig.Load()
}

// Database returns the DB type in configuration.
//获取数据库名称
func Database() string {
	return commonConfig.Config["database"].(string)
}

// MySQL returns the mysql setting in configuration.
//获取数据库设置
func MySQL() MySQLSetting {
	return commonConfig.Config["mysql"].(MySQLSetting)
}

// SQLite returns the SQLite setting
//获取sqlLite配置
func SQLite() SQLiteSetting {
	return commonConfig.Config["sqlite"].(SQLiteSetting)
}

// VerifyRemoteCert returns bool value.
//是否有证书
func VerifyRemoteCert() bool {
	return commonConfig.Config["verify_remote_cert"].(bool)
}

// ExtEndpoint ...

func ExtEndpoint() string {
	return commonConfig.Config["ext_endpoint"].(string)
}

// TokenEndpoint returns the endpoint string of token service, which can be accessed by internal service of Harbor.

func TokenEndpoint() string {
	return commonConfig.Config["token_endpoint"].(string)
}

// LogLevel returns the log level in string format.
func LogLevel() string {
	return commonConfig.Config["log_level"].(string)
}
